package com.libl.service.impl;

import com.libl.domain.Account;
import com.libl.mapper.AccountMapper;
import com.libl.service.AccountService;
import com.libl.testAutowired.DemoInterface;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;

/**
 * @Component 类注解  默认名称是类名首字母小写
 *                   可以通过value属性指定名称，可以省略
 * 有三个子注解：作用与Component一样，用于区分三层架构
 * @Controller 一般用于表现层
 * @Service 一般用于业务层
 * @Repository 一般用于持久层
 *
 * @Value：可以标注在属性上，参数上，方法上等，给简单类型赋值
 *
 * @Autowired：可以标注在属性上，构造方法上，方法上（一般是setter方法），参数上等
 *          一般标注在属性上给属性赋值，可以不需要setter方法
 *          作用：自动装配，可以从IOC容器中找到符合类型的对象注入，只能给引用数据类型赋值，不适用于简单类型
 *          特点：默认根据类型（byType）自动注入，
 *          如果相同类型对象有多个，
 *          1,会自动按照属性名从容器中查找对象
 *          2,可以使用@Qualifier注解指定属性的名称(只能和@Autowired配合使用)
 *          3,在类上标注@Primary 提高此对象的优先级
 * @Autowired的属性：required默认值true，创建IOC容器时必须注入此对象，否则报错
 *
 * @Resource  也是自动装配，需要指定 name属性，默认根据名称（byName）从IOC容器中查找
 *              如果找不到则再根据类型查找
 *
 * @Autowired 和 @Resource的区别：
 *  @Autowired 由spring定义，默认byType注入（实际开发中根据类型注入的情况较多，因为同一类型一般只有一个）
 *  @Resource  由JDK定义，默认byName注入
 *
 * @Scope 两个属性：SINGLETON 单例，默认值 / PROTOTYPE 原型（多例）
 *
 * 相关生命周期的两个注解：@PostConstruct 对象初始化时执行  @PreDestroy 对象销毁时执行
 *
 * @Transactional事务注解：可以标记在类上（对类中所有方法有效）或方法上（仅对单个方法有效,优先级更高）
 *  属性：propagation 传播行为（Required(默认值)/Supports）
 *       isolation  隔离级别
 *       timeout  超时时间（默认值-1）
 *       readonly  是否只读（true/false）
 *       rollback-for 需要回滚的异常（比如：Exception.class）
 *
 * @Configuration：标记该类为配置类（相当于xml）
 *
 * @Bean：方法注解，把方法的返回值注入到IOC容器中，适合创建第三方的类对象
 *        其标记的方法如果参数是引用数据类型，参数上默认有一个@Autowired注解
 *
 * @ComponentScan(value={"com.libl.config","com.libl.service"}):配置要开启注解扫描的包
 *
 * @EnableAspectJAutoProxy：开启aop面向切面编程
 *
 * @EnableTransactionManagement:开启事务
 *
 * @import(value={JdbcConfig.class,MybatisConfig.class}):导入其他配置类,属性value是一个数组
 *
 * @PropertySource("classpath:jdbc.properties")：加载属性文件
 */
@Component
//@Scope("SINGLETON")
public class AccountServiceImpl implements AccountService {

    @PostConstruct
    public void init(){
        System.out.println("对象初始化");
    }
    @PreDestroy
    public void destroy(){
        System.out.println("对象销毁");
    }


    //拿到dao层对象，也就是操作数据库的代理对象
    @Autowired
    private AccountMapper mapper;
    //提供set方法赋值
    /*public void setMapper(AccountMapper mapper) {
        this.mapper = mapper;
    }*/

//    @Autowired
//    DemoInterface demoInterfaceImpl1;

//    @Autowired
//    @Qualifier("demoInterfaceImpl2")
//    DemoInterface demoInterface;

    //@Resource(name="")
    DemoInterface demoInterface;

    @Override
    @Transactional(propagation = Propagation.REQUIRED,
            isolation = Isolation.DEFAULT,
            readOnly = false,
            timeout = -1,
            rollbackFor = Exception.class)//事务注解，标注在方法上仅对此方法有效
    public void transfer(String fromName, String toName, double money) {
        //编程式事务直接写到业务代码里，先开启事务
        //1，根据账户名查找账户
        Account fromAccount = mapper.findByName(fromName);
        Account toAccount = mapper.findByName(toName);
        //2，更改余额
        fromAccount.setMoney(fromAccount.getMoney()-money);
        toAccount.setMoney(toAccount.getMoney()+money);
        //3，更新到数据库
        mapper.update(fromAccount);

        System.out.println(1/0);//用异常测试事务回滚

        mapper.update(toAccount);

        //如果 catch异常  回滚事务
        //再提交事务
    }
}
